const Koa = require('koa');
const koaBody = require('koa-body');
const render = require('koa-art-template');
const static = require('koa-static');
const session = require('koa-session');

const userRouter = require('./routes/user');
const musicRouter = require('./routes/music');

const getUploadFileExt = require('./utils/getUploadFileExt');
const getUploadFileName = require('./utils/getUploadFileName');
const checkDirExists = require('./utils/checkDirExists');

const { port, uploadDir, viewDir } = require('./config');
const { formatError, formatRes } = require('./utils/formatLog');
const rewriteURL = require('./middleware/rewrite');
const error = require('./middleware/errHandle');

let app = new Koa();

// 基于 node_music 字符串进行签名的运算, 为的是保证数据不被篡改
app.keys = ['node_music'];
// 
let store = {
    storage: {},
    set(key, session) {
        this.storage[key] = session;
    },
    get(key) {
        return this.storage[key];
    },
    destroy(key) {
        delete this.storage[key];
    }
}

render(app, {
    root: viewDir,
    extname: '.html',
    debug: process.env.NODE_ENV !== 'production'
});

// 挂载日志模块
app.use(async (ctx, next) => {
    ctx.util = {
        appLogger: require('./config/log4js').appLogger,
        errorLogger: require("./config/log4js").errorLogger,
        assetsLogger: require('./config/log4js').assetsLogger
    }
    await next();
});
app.use(async(ctx, next) => {
    // 请求路由是起始时间
    const start = new Date();
    // 判断是否是静态资源文件
    let isAssetsLog = /\/public\//.test(ctx.url);

    await next();
    // 请求路由结束时间
    const resTime = new Date() - start;
    // 写入日志
    if (isAssetsLog) ctx.util.assetsLogger.info(formatRes(ctx, resTime));
    else ctx.util.appLogger.info(formatRes(ctx, resTime));
});

// 路由参数处理中间件
app.use(koaBody({
    multipart: true,
    parsedMethods: ['GET', 'POST', 'PUT', 'DELETE'],
    formidable: {
        maxFieldsSize: 20 * 1024 * 1024,
        keepExtensions: true,
        uploadDir: uploadDir,
        onFileBegin: (name, file) => {
            // 获取文件后缀
            let ext = getUploadFileExt(file.name);
            // 检查目录是否存在
            checkDirExists(uploadDir);
            // 覆盖 file.path
            file.path = `${uploadDir}\\${getUploadFileName(ext)}`
        }
    }
}));
// 异常处理
app.use(error())
// 重写路由
app.use(rewriteURL(require('./config/rewrite')));
// 静态文件
app.use(static('public'));
// session
app.use(session({ store }, app));
// 判断是否登录（拦截器）
app.use(async (ctx, next) => {
    // 以 /user/ 开头的不需要 验证，直接放行；反之需要验证
    if (ctx.url.startsWith('/user')) {
        await next();
        return;
    }
    // 需要登录
    if (!ctx.session.user) {
        // url 重写
        // ctx.url = '/user/login'
        ctx.body = `
            <div>没有登录 <a href='/user/login'>去登录</a> <a href='/user/register'>去注册</a></div>
        `;
        return;
    }
    await next();
})
// 必须在每次请求中 挂载新的数据与试图的桥梁 (在session之后，router之前) => ctx.state: 给所有页面渲染全局数据
app.use(async (ctx, next) => {
    // ctx.state配置的全局变量我们不仅可以在其他的路由页面使用，还可以在全局模板使用
    ctx.state.user = ctx.session.user;
    await next();
})

// 路由; allowedMethods(): 处理405方法不匹配, 和501方法未实现
app.use(userRouter.routes(), userRouter.allowedMethods());
app.use(musicRouter.routes(), musicRouter.allowedMethods());

app.on('error', (err, ctx) => {
    ctx.util.errorLogger.error(formatError((ctx, err)));
    console.error('server error', err, ctx)
});

app.listen(port, () => {
    console.log(`server running at port ${port}`);
})